"""
selenium基类
本文件存放了selenium基类的封装方法
"""
import time

import win32con
import win32gui
from pywinauto import application
from selenium.common.exceptions import NoSuchElementException, TimeoutException
from selenium.webdriver import Chrome, ActionChains
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from common.processingScreenshots import screenshots
from common.processingLog import log


class Base:
    def __init__(self, driver: Chrome):
        """句柄"""
        self.driver = driver
        # self.timeout = 10
        # self.wait = self.driver.implicitly_wait()

    def get_url(self, url):
        """打开网页"""
        self.driver.maximize_window()
        self.driver.set_page_load_timeout(60)  # 浏览器页面加载最长等待时间60s
        try:
            self.driver.get(url)
            self.driver.implicitly_wait(10)
            log.info("打开网页：{}".format(url))
        except TimeoutException as e:
            log.error("打开{}网页超时请检查网络或网址服务器".format(url))
            raise e

    def close_browser(self):
        """关闭浏览器"""
        time.sleep(1)
        self.driver.quit()
        time.sleep(2)
        return True, '关闭浏览器成功'

    def go_to_sleep(self, time=3):
        """等待"""
        time.sleep(time)
        return True, '等待成功'



    def get_element(self, locator):
        """查找单个元素
        :param locator:目标元素的定位
        :return: 目标元素可定位到则返回定位到的元素，否则报错
        """
        try:
            e = self.driver.find_element(*locator)
            return e
        except NoSuchElementException as e:
            screenshots(driver=self.driver)  # 截图
            log.error("【查找元素失败】，报错原因：" + str(e))  # 日志
            raise e

    def get_elements(self, locator):
        """查找多个相同元素
        :param locator:目标元素的定位
        :return: 目标元素可定位到则返回定位到的元素，否则报错
        """
        try:
            e = self.driver.find_elements(*locator)
            return e
        except NoSuchElementException as e:
            screenshots(driver=self.driver)  # 截图
            log.error("【查找元素失败】，报错原因：" + str(e))  # 日志
            raise e

    def element_num(self, locator):
        """获取相同元素的个数"""
        num = len(self.get_elements(locator))
        log.info("相同元素：{},共{}个".format(locator, num))
        return num

    # *******************************等待************************************************

    def wait_presence_element(self, locator, timeout=30, poll=0.2):
        """
        等待元素出现
        :param locator: 目标元素的定位
        :param timeout: 等待超时的时间
        :param poll: 循环查询的频率
        :return: 目标元素可定位到则返回定位到的元素，否则报错
        """
        try:
            e = WebDriverWait(driver=self.driver, timeout=timeout, poll_frequency=poll).until(
                EC.presence_of_element_located(locator=locator))
            return e
        except TimeoutException as e:
            screenshots(self.driver)
            log.error("【等待元素出现失败】，报错原因：" + str(e))
            raise e

    def wait_clickable_element(self, locator, timeout=30, poll=0.2):
        """等待元素可点击
        :param locator: 目标元素的定位
        :param timeout: 等待超时的时间
        :param poll: 循环查询的频率
        :return: 目标元素可定位到则返回定位到的元素，否则报错
        """
        try:
            e = WebDriverWait(driver=self.driver, timeout=timeout, poll_frequency=poll).until(
                EC.element_to_be_clickable(locator=locator))
            return e
        except TimeoutException as e:
            screenshots(driver=self.driver)  # 截图
            log.error("【等待元素可点击失败】，报错原因：" + str(e))  # 日志
            raise e

    def wait_non_clickable_element(self, locator, timeout=30, poll=0.2):
        """等待元素不可点击
        :param locator: 目标元素的定位
        :param timeout: 等待超时的时间
        :param poll: 循环查询的频率
        :return: 目标元素可定位到则返回定位到的元素，否则报错
        """
        try:
            e = WebDriverWait(driver=self.driver, timeout=timeout, poll_frequency=poll).until_not(
                EC.element_to_be_clickable(locator=locator))
            return e
        except TimeoutException as e:
            screenshots(driver=self.driver)  # 截图
            log.error("【等待元素可点击失败】，报错原因：" + str(e))  # 日志
            raise e

    def wait_visible_element(self, locator, timeout=30, poll=0.2):
        """
        等待元素可见
        :param locator: 目标元素的定位
        :param timeout: 等待超时的时间
        :param poll: 循环查询的频率
        :return: 目标元素可定位到则返回定位到的元素，否则报错
        """
        try:
            e = WebDriverWait(driver=self.driver, timeout=timeout, poll_frequency=poll).until(
                EC.visibility_of_element_located(locator=locator))
            return e
        except TimeoutException as e:
            screenshots(self.driver)
            log.error("【等待元素可见失败】，报错原因：" + str(e))
            raise e

    def wait_invisible_element(self, locator, timeout=30, poll=0.2):
        """
        等待元素不可见
        :param locator: 目标元素的定位
        :param timeout: 等待超时的时间
        :param poll: 循环查询的频率
        :return: 目标元素可定位到则返回定位到的元素，否则报错
        """
        try:
            e = WebDriverWait(driver=self.driver, timeout=timeout, poll_frequency=poll).until_not(
                EC.visibility_of_element_located(locator=locator))
            return e
        except TimeoutException as e:
            screenshots(self.driver)
            log.error("【等待元素可见失败】，报错原因：" + str(e))
            raise e

    # ************************************************************************************
    def is_display_element(self, locator):
        """
        元素是否可见
        :param locator: 目标元素的定位
        :return: true或false，true为可见，false不可见
        """
        e = self.wait_visible_element(locator).is_displayed()
        return e

    # *************************************************************************************

    def input(self, locator, data):
        """
        输入
        :param locator: 目标元素的定位
        :param data: 需要输入的值
        :return:
        """
        e = self.get_element(locator)
        try:
            e.send_keys(data)
        except Exception as e:
            screenshots(self.driver)
            log.error("【输入失败】，报错原因：" + str(e))
            raise e

    def is_clear(self, locator):
        """
        清空输入框
        :param locator: 目标元素的定位
        :return:
        """
        self.get_element(locator).click()
        self.driver.implicitly_wait(0.5)
        log.info("清空输入框！")

    def selector(self, locator, data):
        """
         下拉选择
         :param locator: 目标元素的定位
         :param data: 需要被选择的值
         :return:
         """
        e = self.get_element(locator)
        try:
            Select.select_by_value(data)
        except Exception as e:
            screenshots(self.driver)
            log.error("【选择失败】，报错原因：" + str(e))
            raise e

    def execute_script(self, script):
        """
        执行脚本
        :param script: jquery/javascript脚本
        :return:返回脚本执行后的结果
        """
        e_script = 'return ' + script
        time.sleep(2)
        e = self.driver.execute_script(script=e_script)
        return e

    def result_is_be_true(self, expected, title):
        """
        判断测试结果
        :param expected: 期望结果的脚本，返回True则成功
        :param title: 测试用例的title
        :return:
        """
        try:
            assert expected == True
            print(title + "通过")
            log.info("【auto_test】——用例【{}】通过".format(title))
        except AssertionError as e:
            print(title + "失败")
            log.error("【auto_test】——用例【{}】未通过".format(title))
            raise e

    # *******************************鼠标操作************************************************
    def click(self, locator):
        """
        鼠标操作--点击
        :param locator: 目标元素的定位
        :return:
        """
        e = self.wait_clickable_element(locator)
        e.click()

    def double_click(self, locator):
        """
        鼠标操作--双击
        :param locator: 目标元素的定位
        :return:
        """
        e = self.wait_clickable_element(locator)
        action = ActionChains(self.driver)
        action.double_click(e).perform()

    def context_click(self, locator):
        """
        鼠标操作--右击
        :param locator: 目标元素的定位
        :return:
        """
        e = self.wait_clickable_element(locator)
        action = ActionChains(self.driver)
        action.context_click(e).perform()

    def move_to_element(self, locator):
        """
        鼠标操作--悬浮
        :param locator: 目标元素的定位
        :return:
        """
        e = self.wait_clickable_element(locator)
        action = ActionChains(self.driver)
        action.move_to_element(e).perform()

    def drag_and_drop(self, source_locator, target_locator):
        """
        鼠标操作--把source_locator元素拖到target_locator中
        :param source_locator: 需要拖拽的元素
        :param target_locator: 放置拖拽元素的目标元素
        :return:
        """
        source = self.wait_clickable_element(source_locator)
        target = self.wait_presence_element(target_locator)
        action = ActionChains(self.driver)
        action.drag_and_drop(source=source, target=target).perform()

    def scroll_to_window_bottom(self):
        """滚动到窗口底部"""
        self.driver.execute_script("window.scrollTo(0,document.body.scrollHeight)")



    # *******************************窗口操作************************************************

    def switch_window(self, action, timeout=30, poll=0.2):
        """
         切换浏览器窗口
         :param action: 切换窗口前的操作
         :param timeout: 等待超时的时间
         :param poll: 循环查询的频率
         :return:
         """
        window = self.driver.window_handles  # 操作前的窗口数量
        action  # 进行操作
        try:
            WebDriverWait(driver=self.driver, timeout=timeout, poll_frequency=poll).until(
                EC.new_window_is_opened(window))  # 等待新窗口打开
        except Exception as e:
            screenshots(self.driver)
            log.error("【切换窗口失败】，报错原因：" + str(e))
            raise e
        else:
            self.driver.switch_to.window(self.driver.window_handles[-1])  # 切换到新窗口

    def switch_to_iframe_window(self, locator, timeout=30, poll=0.2):
        """
        iframe窗口切换
        :param locator: 目标元素的定位
        :param timeout: 等待超时的时间
        :param poll: 循环查询的频率
        :return:
        """
        try:
            WebDriverWait(driver=self.driver, timeout=timeout, poll_frequency=poll).until(
                EC.frame_to_be_available_and_switch_to_it(locator=locator))
        except Exception as e:
            screenshots(self.driver)
            log.error("【切换到iframe窗口失败】，报错原因：" + str(e))
            raise e

    def switch_to_parent_iframe_window(self):
        """切换到iframe的上一层窗口"""
        try:
            self.driver.switch_to.parent_frame()
        except Exception as e:
            screenshots(self.driver)
            log.error("【切换到iframe的上一层窗口失败】，报错原因：" + str(e))
            raise e

    def switch_to_alert_window(self):
        """alert窗口切换"""
        try:
            self.driver.switch_to.alert
        except Exception as e:
            screenshots(self.driver)
            log.error("【切换alert窗口失败】，报错原因：" + str(e))
            raise e

    # *******************************************************************************

    def upload_file(self, file):
        """
        上传文件
        :param file: 
        :return: 
        """""
        dialog_class = "#32770"
        file_dir = file
        button_name = "打开(&O)"

        dialog = win32gui.FindWindow(dialog_class, "打开")

        ComboBoxEx32 = win32gui.FindWindowEx(dialog, 0, "ComboBoxEx32", None)  # 二级
        comboBox = win32gui.FindWindowEx(ComboBoxEx32, 0, "ComboBox", None)  # 三级
        # 编辑按钮
        edit = win32gui.FindWindowEx(comboBox, 0, 'Edit', None)  # 四级
        # 打开按钮
        button = win32gui.FindWindowEx(dialog, 0, 'Button', button_name)  # 二级

        win32gui.SendMessage(edit, win32con.WM_SETTEXT, None, file_dir)  # 发送文件路径
        time.sleep(1)
        win32gui.SendMessage(dialog, win32con.WM_COMMAND, 1, button)  # 点击打开按钮

    def upload_file1(self, locator, class_name, file_name):
        """
        上传文件
        :param locator: 目标元素的定位
        :param class_name: 文件选择弹框的id，可用WinSpy定位
        :param file_name: 文件路径
        :return:
        """
        e = self.get_element(locator)  # 上传按钮
        time.sleep(2)
        app = application.Application()
        app.connect(class_name=class_name)  # class_name：弹框的id，可用WinSpy定位
        app["Dialog"]["Edit1"].type_keys(file_name)  # 输入文件
        app["Dialog"]["Button1"].click()

    def upload_file2(self, **kwargs):
        """
        上传文件
        :param kwargs:
        :return:
        """
        try:
            dialog_class = kwargs['type']
            file_dir = kwargs['locator']
            button_name = kwargs['index']
        except KeyError:
            return True, '没传对话框的标记或没传文件路径,'
        if self.deriver_type == "chrome":
            title = "打开"
        elif self.deriver_type == "firefox":
            title = "文件上传"
        elif self.deriver_type == "ie":
            title = "选择要加载的文件"
        else:
            title = ""  # 这里根据其它不同浏览器类型来修改
            # 找元素
            # 一级窗口"#32770","打开"
        dialog = win32gui.FindWindow(dialog_class, title)
        if dialog == 0:
            return False, '传入对话框的class定位器有误'
        # 向下传递
        ComboBoxEx32 = win32gui.FindWindowEx(dialog, 0, "ComboBoxEx32", None)  # 二级
        comboBox = win32gui.FindWindowEx(ComboBoxEx32, 0, "ComboBox", None)  # 三级
        # 编辑按钮
        edit = win32gui.FindWindowEx(comboBox, 0, 'Edit', None)  # 四级
        # 打开按钮

        button = win32gui.FindWindowEx(dialog, 0, 'Button', button_name)  # 二级
        if button == 0:
            return False, '按钮text属性传值有误'
        # 输入文件的绝对路径，点击“打开”按钮
        win32gui.SendMessage(edit, win32con.WM_SETTEXT, None, file_dir)  # 发送文件路径
        time.sleep(1)
        win32gui.SendMessage(dialog, win32con.WM_COMMAND, 1, button)  # 点击打开按钮
        return True, '上传文件成功'
